package com.example.demo.controller;

import jakarta.annotation.Resource;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 订单控制器类，处理与订单相关的请求，包含库存管理和并发控制的方法。
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    /**
     * 存储库存信息的映射，初始库存数量为 100。
     */
    private Map<String, Integer> kucun = new HashMap<String, Integer>() {{
        put("num", 100);
    }};

    /**
     * 另一个库存计数变量，初始值为 5。
     */
    private int count = 5;

    /**
     * 减少库存数量的方法。
     * 检查库存映射中是否存在 "num" 键，如果存在且库存数量不为 0，则将库存数量减 1。
     * 如果库存数量为 0，则打印 0 并返回。
     */
    public void number() {
        // 检查键是否存在，若存在则减少库存
        if (kucun.containsKey("num")) {
            if (kucun.get("num") == 0) {
                System.out.println(0);
                return;
            }
            kucun.put("num", kucun.get("num") - 1);
        }
    }

    /**
     * 处理 "/order/substraction" 请求的方法。
     * 调用 number 方法减少库存，并打印当前库存数量。
     */
    @GetMapping("/substraction")
    public void substraction() {
        this.number();
        System.out.println(kucun.get("num"));
    }



    /**
     * 可重入锁，用于并发控制。
     */
    private final ReentrantLock lock = new ReentrantLock();

    /**
     * 尝试获取锁并减少库存数量的方法。
     * 如果成功获取锁，检查库存数量是否为 0，如果不为 0 则将库存数量减 1 并打印新的库存数量。
     * 如果库存数量为 0，则打印 "没有库存了" 并返回。
     */
    public void reduce() {
        if (lock.tryLock()) {
            try {
                if (count == 0) {
                    System.out.println("没有库存了");
                    return;
                }
                count--;
                System.out.println(count);
            } finally {
                lock.unlock();
            }
        } else {
            //System.out.println("没有获取锁");
        }
    }

    /**
     * 设置超时时间尝试获取锁并减少库存数量的方法。
     * 防止有些业务长时间占用锁，超时时间为 2000 毫秒。
     * 如果成功获取锁，检查库存数量是否为 0，如果不为 0 则将库存数量减 1 并打印新的库存数量。
     * 如果库存数量为 2，则线程休眠 3000 毫秒。
     * 如果库存数量为 0，则打印 "没有库存了" 并返回。
     * 如果在超时时间内未获取到锁，则打印相应信息。
     *
     * @throws InterruptedException 如果在等待锁的过程中线程被中断。
     */
    public void reduce2() throws InterruptedException {
        if (lock.tryLock(2000, TimeUnit.MILLISECONDS)) {
            try {
                if (count == 0) {
                    System.out.println("没有库存了");
                    return;
                }
                if (count == 2) {
                    Thread.sleep(3000); // 如果不加超时时间，执行时间至少5s
                }
                count--;
                System.out.println("count为" + count + "");
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("当count为" + count + "时，没有获取锁");
        }
    }

    /**
     * 用于 10 个并发场景下减少库存数量的方法。
     * 先获取锁，检查库存数量是否为 0，如果不为 0 则将库存数量减 1 并打印新的库存数量。
     * 如果库存数量为 0，则打印 "没有库存了" 并返回。
     * 最后释放锁。
     */
    public void reduce3() {
        lock.lock();
        try {
            if (count == 0) {
                System.out.println("没有库存了");
                return;
            }
            count--;
            System.out.println(count);
        } finally {
            lock.unlock();
        }
    }


    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public void redisLock() throws InterruptedException {
        String key = "order:lock:123";
        String threadId = String.valueOf(Thread.currentThread().getId());
        int start = 0;
        int maxrun = 3;
        while (start <= maxrun) {
            try {
                // 尝试获取锁，如果锁不存在则设置锁并设置过期时间为 1 秒
                Boolean aBoolean1 = stringRedisTemplate.opsForValue().setIfAbsent(key, threadId, 30, TimeUnit.SECONDS);
                if (aBoolean1 != null && aBoolean1) {
                    if (count == 0) {
                        System.out.println("没有库存了");
                        return;
                    }
                    Thread.sleep(100);
                    count--;
                    System.out.println("count为" + count + "");
                } else {
                    System.out.println("没有获取锁");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                // 释放锁，确保当前线程的锁被释放
                if (threadId.equals(stringRedisTemplate.opsForValue().get(key))) {
                    stringRedisTemplate.delete(key);
                }
            }
            start++;
            Thread.sleep(500);
        }
    }


    /**
     * 可重入锁，用于并发控制。
     */
    private static final ReentrantLock lock2 = new ReentrantLock();
    private static int count2 = 5;

    /**
     * 用于 10 个并发场景下减少库存数量的方法。
     * 先获取锁，检查库存数量是否为 0，如果不为 0 则将库存数量减 1 并打印新的库存数量。
     * 如果库存数量为 0，则打印 "没有库存了" 并返回。
     * 最后释放锁。
     */
    public static void reduce4(String name) {
        if (lock2.tryLock()) {
            try {
                if (count2 == 0) {
                    System.out.println(name + "没有库存了");
                    return;
                }
                count2--;
                System.out.println(name + ":" + count2);

            } finally {
                lock2.unlock();
            }
        }
    }

    /**
     * 用于 10 个并发场景下减少库存数量的方法。
     * 先获取锁，检查库存数量是否为 0，如果不为 0 则将库存数量减 1 并打印新的库存数量。
     * 如果库存数量为 0，则打印 "没有库存了" 并返回。
     * 最后释放锁。
     */
    public static void reduce5(String name) {
        lock2.lock();
        try {
            if (count2 == 0) {
                System.out.println(name + "没有库存了");
                return;
            }
            count2--;
            System.out.println(name + ":" + count2);
        } finally {
            lock2.unlock();
        }
    }



    @Resource
    private RedissonClient redissonClient;
    private volatile int count3 = 5;



    public void redissonReduce() throws InterruptedException {
        RLock lock = redissonClient.getLock("order:lock"+"123:");
        boolean b = lock.tryLock(3, 3, TimeUnit.SECONDS);
        if (b) {
            try {
                if (count3 == 0){
                    System.out.println("库存不足");
                    return;
                }
                count3--;
                Thread.sleep(100);
                System.out.println("线程"+Thread.currentThread().getId()+":"+count3);
            } catch (Exception e) {

            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("线程"+Thread.currentThread().getId()+":"+"获取锁失败");
        }
    }


    /**
     * 处理 "/order/substraction2" 请求的方法。
     * 调用 reduce2 方法减少库存。
     *
     * @throws InterruptedException 如果在等待锁的过程中线程被中断。
     */
    @GetMapping("/substraction2")
    public void substraction2() throws InterruptedException {
        //this.reduce2();
        //this.redisLock();

        this.redissonReduce();

    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            int loop = 10;
            while (loop > 0) {
                reduce4("线程111");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                loop--;
            }
        });
        Thread thread2 = new Thread(() -> {
            int loop = 10;
            while (loop > 0) {
                reduce4("线程222");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                loop--;
            }
        });
        Thread thread3 = new Thread(() -> {
            int loop = 10;
            while (loop > 0) {
                reduce4("线程333");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                loop--;
            }
        });
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
